.Net5 下Dictionary 为什么可以在foreach中Remove

您所在的位置:网站首页 dictionary remove .Net5 下Dictionary 为什么可以在foreach中Remove

.Net5 下Dictionary 为什么可以在foreach中Remove

2024-07-15 12:45| 来源: 网络整理| 查看: 265

  在一个讨论群里,看见有人说Dictionary可以在foreach中直接调用Remove了,带着疑问,写了简单代码进行尝试

  

class Program { static void Main(string[] args) { var dic = Enumerable.Range(1, 10).ToDictionary(t => t, t => t); foreach (var i in dic) { if (i.Key.GetHashCode() % 2 == 0) { dic.Remove(i.Key); } else { Console.WriteLine($"{i.Key}"); } } Console.WriteLine("Hello World!"); } }

  执行果然没有报错,输出正常。

  

 

  终于不再需要进行单独执行Remove

 

  要想知道为啥在.Net Framework上不行,在.Net5下却可以,就需要知道在.Net5中Dictionary有着什么样的变化

  .Net Framework中源码         .Net5中源码

       我们看下两者有什么区别:

  Framework中是这样的:

  

1 public bool MoveNext() { 2 if (version != dictionary.version) { 3 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); 4 } 5 6 // Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends. 7 // dictionary.count+1 could be negative if dictionary.count is Int32.MaxValue 8 while ((uint)index < (uint)dictionary.count) { 9 if (dictionary.entries[index].hashCode >= 0) { 10 current = new KeyValuePair(dictionary.entries[index].key, dictionary.entries[index].value); 11 index++; 12 return true; 13 } 14 index++; 15 } 16 17 index = dictionary.count + 1; 18 current = new KeyValuePair(); 19 return false; 20 } View Code

  

  .Net5中是这样的:

  

1 public bool MoveNext() 2 { 3 if (_version != _dictionary._version) 4 { 5 ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); 6 } 7 8 // Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends. 9 // dictionary.count+1 could be negative if dictionary.count is int.MaxValue 10 while ((uint)_index < (uint)_dictionary._count) 11 { 12 ref Entry entry = ref _dictionary._entries![_index++]; 13 14 if (entry.next >= -1) 15 { 16 _current = new KeyValuePair(entry.key, entry.value); 17 return true; 18 } 19 } 20 21 _index = _dictionary._count + 1; 22 _current = default; 23 retur View Code

 

  细看好像两者并没什么很明显的区别。我们知道,在对Dictionary进行操作的时候,_version会自增改变,从而导致报错。难道.Net5中进行Remove操作_version不会改变。

  .Net5中Remove代码:

  

1 public bool Remove(TKey key) 2 { 3 // The overload Remove(TKey key, out TValue value) is a copy of this method with one additional 4 // statement to copy the value for entry being removed into the output parameter. 5 // Code has been intentionally duplicated for performance reasons. 6 7 if (key == null) 8 { 9 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); 10 } 11 12 if (_buckets != null) 13 { 14 Debug.Assert(_entries != null, "entries should be non-null"); 15 uint collisionCount = 0; 16 uint hashCode = (uint)(_comparer?.GetHashCode(key) ?? key.GetHashCode()); 17 ref int bucket = ref GetBucket(hashCode); 18 Entry[]? entries = _entries; 19 int last = -1; 20 int i = bucket - 1; // Value in buckets is 1-based 21 while (i >= 0) 22 { 23 ref Entry entry = ref entries[i]; 24 25 if (entry.hashCode == hashCode && (_comparer?.Equals(entry.key, key) ?? EqualityComparer.Default.Equals(entry.key, key))) 26 { 27 if (last < 0) 28 { 29 bucket = entry.next + 1; // Value in buckets is 1-based 30 } 31 else 32 { 33 entries[last].next = entry.next; 34 } 35 36 Debug.Assert((StartOfFreeList - _freeList) < 0, "shouldn't underflow because max hashtable length is MaxPrimeArrayLength = 0x7FEFFFFD(2146435069) _freelist underflow threshold 2147483646"); 37 entry.next = StartOfFreeList - _freeList; 38 39 if (RuntimeHelpers.IsReferenceOrContainsReferences()) 40 { 41 entry.key = default!; 42 } 43 44 if (RuntimeHelpers.IsReferenceOrContainsReferences()) 45 { 46 entry.value = default!; 47 } 48 49 _freeList = i; 50 _freeCount++; 51 return true; 52 } 53 54 last = i; 55 i = entry.next; 56 57 collisionCount++; 58 if (collisionCount > (uint)entries.Length) 59 { 60 // The chain of entries forms a loop; which means a concurrent update has happened. 61 // Break out of the loop and throw, rather than looping forever. 62 ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported(); 63 } 64 } 65 } 66 return false; 67 }

  一看果然_version不会变化。看到这可能会直呼内行啊,一行代码就解决问题,那为什么Framework中不这样做呢。相关提交讨论

  

   


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3